<?php
if (!class_exists('AlfaController')) {
	
	/**
	 * Main view controller
	 * @author Alecsandru Gabriel DINU
	 * @since Jun 12, 2008
	 */
	class AlfaController {
		private static $theInstance;
		private $configFile = "controller.xml";
		private $devMode = false;
		private $actionData;
		private $log;
	  
		/**
		 * Class constructor
		 * @access private
		 * @param boolean
		 */
		private function __construct($devMode) {
			$this->devMode = $devMode;
			$this->log = Logger::getInstance()->getLogger();
			$this->load();
		}

		/**
		 * Static factory method
		 * @access public
		 * @param boolean
		 * @return AlfaController
		 */
		public static function getInstance($devMode) {
			if (!self::$theInstance) {
				self::$theInstance = new AlfaController($devMode);
			}
			return self::$theInstance;
		}

		/**
		 * Loads controller data
		 * @access public
		 */
		private function load() {
			// load action controller configuration
			$this->actionData = new AlfaControllerData($this->devMode);
			$this->actionData->load();
		}

	  
		/**
		 * Action dispatcher
		 * @access public
		 * @param Context
		 */
		public function dispatch(Context $context) {
			if (!($context instanceof Context)) {
				throw new IllegalArgumentException("Invalid parameter. Expected Context object.");
			}
			
			$config = AlfaConfiguration::getInstance();
			
			$actionName = $context->getAction();
			
			if ($actionName == '') {
				$config = AlfaConfiguration::getInstance();
				$actionName = $config->getString("webapp/controller/defaultAction", "index");
			}
			
			try {
				$action = $this->actionData->getAction($actionName);
			} catch (AlfaControllerException $e) {
				if (StringUtils::endsWith("/", $actionName)) {
					try {
						$defaultAction = AlfaConfiguration::getInstance()->getString("webapp/controller/defaultAction", "index");
						$action = $this->actionData->getAction($actionName . $defaultAction);
					} catch (AlfaControllerException $e) {
						throw new ResourceNotFoundException($e);
					}
				} else {
					throw new ResourceNotFoundException($e);
				}
			}
			
			try {
				$class = AlfaBeanLoader::getInstance()->getBeanReflection((string)$action["class"]);
				$actionClass = AlfaBeanLoader::getInstance()->getBean((string)$action["class"]);
			} catch (NoSuchBeanDefinitionException $e) {
				// it's not a bean, it a class name
				$class = new ReflectionClass((string)$action["class"]);
				$actionClass = $class->newInstance();		
			} catch (BeanCreationException $e) {
				$this->log->error($e);
				$actionClass = $class->newInstance();
			}
			
			$actionClass->setContext($context);
			
			// Call setters
			$methods = $class->getMethods();
			foreach ($methods as $method) {
				if (ereg ("set([A-Z]{1})([a-zA-Z0-9]+)", $method->getName(), $regs)) {
					$attribute = strtolower($regs[1]) . $regs[2];
					if ($context->getRequest()->containsKey($attribute)) {
						$method->invoke($actionClass, $context->getRequest()->getString($attribute));
					}
				}
			}
			
			// get the name of the method to call
			// if there is a parameter in the request with the name method:{methodname},
			// then  {methodname} will be called instead of the defined method of the URL
			$actionMethod = "";
			if ($_SERVER['REQUEST_METHOD'] == 'POST') {
				$attributes = $context->getRequest()->getParameters();
				if (($key = $context->getRequest()->containsKeyLike("method:(.*)"))) {
					ereg ("method:(.*)", $key, $regs);
					$actionMethod = $regs[1];
				}
			}
			if (StringUtils::isBlank($actionMethod)) {
				$actionMethod = $action["method"];
			}
			
			// call action method
			$method = $class->getMethod($actionMethod);
			$resultName = $method->invoke($actionClass);

			// finalize action
			$method = $class->getMethod("finalize");
			$method->invoke($actionClass);
			
			
			$result = $this->actionData->getActionResult($action, $resultName);
			
			if ($result["type"] == '') {
				// Call getter fields and insert the object in the template
				$getters = array();
				foreach ($methods as $method) {
					if (ereg ("get([A-Z]{1})([a-zA-Z0-9]+)", $method->getName(), $regs)) {
						$attribute = strtolower($regs[1]) . $regs[2];
						$getter = $method->invoke($actionClass);
						$getters[$attribute] = $getter;
					}
				}
				
			
				// decorate the page
				$decorator = new AlfaDecorator($this->devMode);
				$decorator->setGetters($getters);
				$decorator->setContext($context);
				$decorator->setTranslator(AlfaTranslator::getInstance($context));
				$decorator->decorate($result["value"]);
				
			} else if ($result["type"] == "redirect-action") {
				$context->getRequest()->setString('_ac', $result["value"]);
				$context->parseActionFromRequest();
				
				$this->dispatch($context);
			} else if ($result["type"] == "redirect") {
				$context->redirect($result["value"]);
			}
			
		}
	  
		
		/**
		 * Forces the config file to be read again
		 * @access public
		 */
		public function reload() {
			// load action controller configuration
			$this->actionData = new AlfaControllerData(true);
			$this->actionData->load();
			
			// load decorator data
			$decorator = new AlfaDecorator(true);
		}
	  
	}
}
?>